import { Injectable, Inject } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { difference, filter, includes, isEmpty, map } from 'lodash';

import SysRole from '@main/entities/service/admin/sys/sys-role.entity';
import SysRoleMenu from '@main/entities/service/admin/sys/sys-role-menu.entity';
import SysUserRole from '@main/entities/service/admin/sys/sys-user-role.entity';
import { EntityManager, In, Like, Not, Repository } from 'typeorm';
import { ROOT_ROLE_ID } from '@main/service/modules/admin/admin.constants';
import { ApiException } from '@main/common/exceptions/api.exception';

import { SysOperationLogService } from '../operationLog/operationLog.service';

@Injectable()
export class SysRoleService {
  constructor(
    @InjectRepository(SysRole) private roleRepository: Repository<SysRole>,
    @InjectRepository(SysRoleMenu)
    private roleMenuRepository: Repository<SysRoleMenu>,
    @InjectRepository(SysUserRole)
    private userRoleRepository: Repository<SysUserRole>,
    @InjectEntityManager() private entityManager: EntityManager,
    private operationLogService: SysOperationLogService, 
    @Inject(ROOT_ROLE_ID) private rootRoleId: number
  ) {}

  /**
   * 列举所有角色：除去超级管理员
   */
  async list() {
    return await this.roleRepository.find({
      where: { id: Not(this.rootRoleId.toString()) },
    });
  }

  /**
   * 列举所有角色条数：除去超级管理员
   */
  async count(): Promise<number> {
    const count = await this.roleRepository.count({
      where: { id: Not(this.rootRoleId.toString()) },
    });
    return count;
  }

  /**
   * 分页加载角色信息
   */
  async page(data) {
    const { pageSize, pageNum, entity = {} } = data;

    const { name = '', remark = '' } = entity;

    return await this.roleRepository.findAndCount({
      where: {
        id: Not(this.rootRoleId.toString()),
        name: Like(`%${name}%`),
        remark: Like(`%${remark}%`),
      },
      order: {
        updateTime: 'DESC',
      },
      take: pageSize,
      skip: (pageNum - 1) * pageSize,
    });
  }

  /**
   * 增加角色
   */
  async add(data, user, mac, ip, syncHostSqlite) {
    const { name, remark, permissionCodeList } = data;

    const databaseOperationLogList = [];

    const role = await this.roleRepository.insert({
      name,
      remark,
      userId: `${user.id}`,
      createName: user.nickName,
      createUserName: user.userName,
      updateName: user.nickName,
      updateUserName: user.userName,
    });

    databaseOperationLogList.push({
      type: 'add',
      dataType: 'obj',
      database: 'SysRole',
      checkOnlyKeyList: ['name'],
      data: role,
      whereObj: null,
      remark: `SysRole表新增数据`,
    });
    await this.operationLogService.add(
      {
        type: 'add',
        msg: `角色模块新增角色`,
        databaseOperationLogList,
        remark: `新增角色${name}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );

    const { identifiers } = role;

    const roleId = parseInt(identifiers[0].id);

    if (permissionCodeList && permissionCodeList.length > 0) {
      // 关联菜单
      const insertRows = permissionCodeList.map((m) => {
        return {
          roleId,
          menuCode: m,
        };
      });
      await this.roleMenuRepository.insert(insertRows);
    }
  }

  /**
   * 更新角色信息
   */
  async update(data, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];
    const { id: roleId, name, remark, permissionCodeList } = data;

    await this.roleRepository.update(
      { id: roleId },
      {
        name,
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      }
    );

    databaseOperationLogList.push({
      type: 'update',
      dataType: 'obj',
      database: 'SysRole',
      checkOnlyKeyList: [],
      data: {
        name,
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      },
      whereObj: { id: roleId },
      remark: `SysRole表更新角色信息`,
    });

    const originMenuRows = await this.roleMenuRepository.find({
      where: { roleId },
    });
    const originMenuIds = originMenuRows.map((e) => {
      return e.menuCode;
    });

    // 开始对比差异
    const insertMenusRowIds = difference(permissionCodeList, originMenuIds);
    const deleteMenusRowIds = difference(originMenuIds, permissionCodeList);

    // using transaction
    await this.entityManager.transaction(async (manager) => {
      // 菜单
      if (insertMenusRowIds.length > 0) {
        // 有条目更新
        const insertRows = insertMenusRowIds.map((e) => {
          return {
            roleId,
            menuCode: e,
          };
        });
        await manager.insert(SysRoleMenu, insertRows);
      }
      if (deleteMenusRowIds.length > 0) {
        // 有条目需要删除
        const realDeleteRowIds = filter(originMenuRows, (e) => {
          return includes(deleteMenusRowIds, e.menuCode);
        }).map((e) => {
          return e.id;
        });
        await manager.delete(SysRoleMenu, realDeleteRowIds);
      }
    });

    await this.operationLogService.add(
      {
        type: 'update',
        msg: `角色模块更新角色信息`,
        databaseOperationLogList,
        remark: `更新用户${name}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 根据角色Id数组删除
   */
  async delete(roleIdList, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];
    if (includes(roleIdList, 0)) {
      throw new Error('Not Support Delete Root');
    }
    await this.entityManager.transaction(async (manager) => {
      await manager.delete(SysRole, roleIdList);
      databaseOperationLogList.push({
        type: 'delete',
        dataType: 'array',
        database: 'SysRole',
        checkOnlyKeyList: [],
        data: null,
        whereObj: roleIdList,
        remark: `SysRole表删除用户`,
      });
      await manager.delete(SysRoleMenu, { roleId: In(roleIdList) });
    });
    await this.operationLogService.add(
      {
        type: 'delete',
        msg: `角色模块删除角色`,
        databaseOperationLogList,
        remark: `删除角色`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );
  }

  /**
   * 根据角色ID列表查找关联用户ID
   */
  async countUserIdByRole(ids: number[]): Promise<number | never> {
    if (includes(ids, this.rootRoleId)) {
      throw new Error('Not Support Delete Root');
    }
    return await this.userRoleRepository.count({ where: { roleId: In(ids) } });
  }

  /**
   * 根据用户id查找角色信息
   */
  async getRoleIdByUser(id: string): Promise<string[]> {
    const result = await this.userRoleRepository.find({
      where: {
        userId: id,
      },
    });
    if (!isEmpty(result)) {
      return map(result, (v) => {
        return v.roleId;
      });
    }
    return [];
  }

  /**
   * 根据角色获取角色信息
   */
  async info(rid: string) {
    const menus = await this.roleMenuRepository.find({
      where: { roleId: rid },
    });

    return menus.map((item) => item.menuCode);
  }

  async isExistKey(name: string) {
    const result = await this.roleRepository.findOne({ where: { name } });

    if (result) {
      throw new ApiException(10036);
    }
  }
}
